package com.leyou.search.service.impl;

import com.leyou.common.advice.LyException;
import com.leyou.item.client.ItemClients;
import com.leyou.item.dto.BrandDTO;
import com.leyou.item.dto.CategoryDTO;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.entity.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.*;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository repository;

    @Autowired
    private ItemClients itemClients;

    @Override
    public Mono<List<String>> suggest(String key) {

        return repository.suggestBySingleField("suggestion", key);
    }

    @Override
    public Mono<PageInfo<Goods>> querylist(SearchRequest searchRequest) {

        //添加各种条件
        SearchSourceBuilder sourceBuider = new SearchSourceBuilder();
        //添加查询
       QueryBuilder queryBuilder = querybuilder(searchRequest);
        sourceBuider.query(queryBuilder);
        //设置分页
        int size = searchRequest.getRows();
        int from = (searchRequest.getPage()-1)*size;

        sourceBuider.from(from);
        sourceBuider.size(size);
        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<am>");
        highlightBuilder.postTags("</am>");
        highlightBuilder.field("title");

        sourceBuider.highlighter(highlightBuilder);
        //执行查询
        return this.repository.queryBySourceBuilderForPageHighlight(sourceBuider);

    }

    private QueryBuilder querybuilder(SearchRequest searchRequest){
        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)){
            throw new LyException(400, "请求参数有误！");
        }
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //给布尔查询中添加普通查询条件
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", key).operator(Operator.AND));

        Map<String, String> filters = searchRequest.getFilters();

        //从过滤条件中执行过滤
        if (!CollectionUtils.isEmpty(filters)){
            filters.entrySet().forEach(entry ->{
                String entryKey = entry.getKey();
                String entryValue = entry.getValue();
             if ("品牌".equals(entryKey)){
                entryKey = "brandId";
                boolQueryBuilder.filter(QueryBuilders.termQuery(entryKey, entryValue));
             }else if ("分类".equals(entryKey)){
                 entryKey = "categoryId";
                 boolQueryBuilder.filter(QueryBuilders.termQuery(entryKey, entryValue));
             }
            }) ;
        }


        return boolQueryBuilder;
    }

    @Override
    public Mono<Map<String,List<?>>> filterlist(SearchRequest searchRequest) {
        //添加各种条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
       //开启查询
        QueryBuilder queryBuilder = querybuilder(searchRequest);
        sourceBuilder.query(queryBuilder);

        //添加聚合条件
        sourceBuilder.aggregation(AggregationBuilders.terms("brandagg").field("brandId").size(20));
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryagg").field("caretoryId").size(20));

        //添加聚合条件
        sourceBuilder.aggregation(
                AggregationBuilders.nested("specsAgg", "specs")
                        .subAggregation(AggregationBuilders.terms("nameAgg").field("specs.name")
                                .subAggregation(AggregationBuilders.terms("valueAgg").field("specs.value"))));

        //执行聚合
        Mono<Aggregations> aggregationsMono = this.repository.aggregationBySourceBuilder(sourceBuilder);

        //不订阅，不重新封装，直接转换
        Mono<Map<String, List<?>>> map = aggregationsMono.map(aggregations -> {

            Map<String, List<?>> hashMap = new LinkedHashMap<>();

            Terms brandagg = aggregations.get("brandagg");
            List<Long> brandids = brandagg
                    .getBuckets()
                    .stream()
                    .map(bucket -> bucket.getKeyAsNumber())
                    .map(Number::longValue)
                    .collect(Collectors.toList());

            Terms categoryagg = aggregations.get("categoryagg");
            List<Long> categoryids = categoryagg
                    .getBuckets()
                    .stream()
                    .map(bucket -> ((Terms.Bucket) bucket).getKeyAsNumber())
                    .map(Number::longValue)
                    .collect(Collectors.toList());

            //根据聚合的参数查询对应的集合 ， 并且判断其健壮性

            if (!CollectionUtils.isEmpty(brandids)) {
                List<BrandDTO> brandDTOS = this.itemClients.querybrandlist(brandids);
                hashMap.put("品牌", brandDTOS);
            }

            if (!CollectionUtils.isEmpty(categoryids)) {
                List<CategoryDTO> categoryDTOS = this.itemClients.queryCategoryByIds(categoryids);
                hashMap.put("分类", categoryDTOS);
            }


            //根据聚合名称获取nested聚合
            Nested specsAgg = aggregations.get("specsAgg");

            //根据specsAgg获取子聚合
            Terms nameAgg = specsAgg.getAggregations().get("nameAgg");

            nameAgg.getBuckets().forEach(bucket ->{
              String key = ((Terms.Bucket) bucket).getKeyAsString();

                //根据nameAgg获取其子聚合
                Terms valueAgg = ((Terms.Bucket) bucket).getAggregations().get("valueAgg");

                //把valueAgg中聚合结果取出封装成list
                List<String> valuelist = valueAgg.getBuckets()
                        .stream()
                        .map(bucketvalue -> ((Terms.Bucket) bucketvalue).getKeyAsString())
                        .collect(Collectors.toList());
                hashMap.put(key, valuelist);
            });

            return hashMap;
        });
        return map;
    }
}
